#include "PrimaryGeometry.h"

#pragma region simpledrawing

static void createCircleTable(double **sint,double **cost,const int n)
{
	int i;

	/* Table size, the sign of n flips the circle direction */

	const int size = abs(n);

	/* Determine the angle between samples */

	const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );

	/* Allocate memory for n samples, plus duplicate of first entry at the end */

	*sint = (double *) calloc(sizeof(double), size+1);
	*cost = (double *) calloc(sizeof(double), size+1);
	
	/* Bail out if memory allocation fails, fgError never returns */

	if (!(*sint) || !(*cost))
	{
		free(*sint);
		free(*cost);
	}

	/* Compute cos and sin around the circle */

	(*sint)[0] = 0.0;
	(*cost)[0] = 1.0;

	for (i=1; i<size; i++)
	{
		(*sint)[i] = sin(angle*i);
		(*cost)[i] = cos(angle*i);
	}

	/* Last sample is duplicate of the first */

	(*sint)[size] = (*sint)[0];
	(*cost)[size] = (*cost)[0];
}

// Ve hinh cau dac 
void drawSolidSphere(GLdouble radius, GLint slices, GLint stacks)
{
	glColor3f(0,1,0);  // dat mau de ve: bien vao float tu 0 den 1
	int i,j;

	/* Adjust z and radius as stacks are drawn. */
	double z0,z1;
	double r0,r1;

	/* Pre-computed circle */
	double *sint1,*cost1;
	double *sint2,*cost2;

	createCircleTable(&sint1,&cost1,-slices);  // Tao bang sin cos de ve
	createCircleTable(&sint2,&cost2,stacks*2);

	/* The top stack is covered with a triangle fan */
	z0 = 1.0;
	z1 = cost2[(stacks>0)?1:0];
	r0 = 0.0;
	r1 = sint2[(stacks>0)?1:0];

	glBegin(GL_TRIANGLE_FAN);

	glNormal3d(0,0,1);
	glVertex3d(0,0,radius);

	for (j=slices; j>=0; j--)
	{
		glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
		glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
	}

	glEnd();

	/* Cover each stack with a quad strip, except the top and bottom stacks */
	for( i=1; i<stacks-1; i++ )
	{
		z0 = z1; z1 = cost2[i+1];
		r0 = r1; r1 = sint2[i+1];

		glBegin(GL_QUAD_STRIP);
		for(j=0; j<=slices; j++)
		{
			glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
			glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
			glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
			glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
		}
		glEnd();
	}

	/* The bottom stack is covered with a triangle fan */
	z0 = z1;
	r0 = r1;

	glBegin(GL_TRIANGLE_FAN);

	glNormal3d(0,0,-1);
	glVertex3d(0,0,-radius);

	for (j=0; j<=slices; j++)
	{
		glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
		glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
	}

	glEnd();

	/* Release sin and cos tables */
	free(sint1);
	free(cost1);
	free(sint2);
	free(cost2);
}

// Ve cau luoi
void drawWireSphere(GLdouble radius, GLint slices, GLint stacks)
{
	int i,j;

	/* Adjust z and radius as stacks and slices are drawn. */
	double r;
	double x,y,z;
	/* Pre-computed circle */
	double *sint1,*cost1;
	double *sint2,*cost2;
	createCircleTable(&sint1,&cost1,-slices  );
	createCircleTable(&sint2,&cost2, stacks*2);

	/* Draw a line loop for each stack */
	for (i=1; i<stacks; i++)
	{
		z = cost2[i];
		r = sint2[i];
		glBegin(GL_LINE_LOOP);
		for(j=0; j<=slices; j++)
		{
			x = cost1[j];
			y = sint1[j];
			glNormal3d(x,y,z);
			glVertex3d(x*r*radius,y*r*radius,z*radius);
		}
		glEnd();
	}

	/* Draw a line loop for each slice */
	for (i=0; i<slices; i++)
	{
		glBegin(GL_LINE_STRIP);
		for(j=0; j<=stacks; j++)
		{
			x = cost1[i]*sint2[j];
			y = sint1[i]*sint2[j];
			z = cost2[j];
			glNormal3d(x,y,z);
			glVertex3d(x*radius,y*radius,z*radius);
		}
		glEnd();
	}
	
	/* Release sin and cos tables */
	free(sint1);
	free(cost1);
	free(sint2);
	free(cost2);
}

// Ve hinh tru dac
void drawSolidCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
{
	int i,j;

	/* Step in z and radius as stacks are drawn. */

	double z0,z1;
	const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );

	/* Pre-computed circle */

	double *sint,*cost;

	createCircleTable(&sint,&cost,-slices);

	/* Cover the base and top */

	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0, 0.0, -1.0 );
	glVertex3d(0.0, 0.0,  0.0 );
	for (j=0; j<=slices; j++)
		glVertex3d(cost[j]*radius, sint[j]*radius, 0.0);
	glEnd();

	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0, 0.0, 1.0   );
	glVertex3d(0.0, 0.0, height);
	for (j=slices; j>=0; j--)
		glVertex3d(cost[j]*radius, sint[j]*radius, height);
	glEnd();

	/* Do the stacks */

	z0 = 0.0;
	z1 = zStep;

	for (i=1; i<=stacks; i++)
	{
		if (i==stacks)
			z1 = height;

		glBegin(GL_QUAD_STRIP);
		for (j=0; j<=slices; j++ )
		{
			glNormal3d(cost[j],				sint[j],        0.0 );
			glVertex3d(cost[j]*radius,		sint[j]*radius, z0  );
			glVertex3d(cost[j]*radius, 		sint[j]*radius, z1  );
		}
		glEnd();

		z0 = z1; z1 += zStep;
	}

	/* Release sin and cos tables */

	free(sint);
	free(cost);
}

// Ve hinh Non
void drawSolidCone( GLdouble base, GLdouble small_base, GLdouble height, GLint slices)
{
	int j;

	/* Step in z and radius as stacks are drawn. */

	double z0,z1;
	double r0,r1,r2;

	const double zStep = height;
	const double rStep = (base-small_base);

	/* Scaling factors for vertex normals */

	const double cosn = ( height / sqrt ( height * height + base * base ));
	const double sinn = ( base   / sqrt ( height * height + base * base ));

	/* Pre-computed circle */

	double *sint,*cost;

	createCircleTable(&sint,&cost,-slices);

	/* Cover the circular base with a triangle fan... */

	z0 = 0.0;
	z1 = zStep;

	r0 = base;
	r1 = r0 - rStep;
	r2 = small_base;


	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0,0.0,-1.0);
	glVertex3d(0.0,0.0, z0 );
	for (j=0; j<=slices; j++)
		glVertex3d(cost[j]*r0, sint[j]*r0, z0);
	glEnd();

	/* draw small base*/
	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0,0.0,-1.0);
	glVertex3d(0.0,0.0, z1 );
	for (j=0; j<=slices; j++)
		glVertex3d(cost[j]*r2, sint[j]*r2, z1);
	glEnd();
	
	/* Cover each stack with a quad strip, except the top stack */
	
	glBegin(GL_QUAD_STRIP);

	for(j=0; j<=slices; j++)
	{
		glNormal3d(cost[j]*cosn, sint[j]*cosn, sinn);
		glVertex3d(cost[j]*r0,   sint[j]*r0,   z0  );
		glVertex3d(cost[j]*r1,   sint[j]*r1,   z1  );
	}

	glEnd();
	/* Release sin and cos tables */

	free(sint);
	free(cost);
}

// Ve hinh non da xoay lai truc cho hop voi mo hinh ban tay
void drawSolidConeRotated( GLdouble base, GLdouble small_base, GLdouble height, GLint slices)
{
	glPushMatrix();
	glColor3f(0,0,1);
	glRotated(-90,1,0,0);
	drawSolidCone( base, small_base, height, slices);
	glPopMatrix();
}

// Ve elipsoid
void drawWireElipse(GLdouble radiusX, GLdouble radiusY, GLdouble radiusZ, GLint slices, GLint stacks)
{
	int i,j;

	/* Adjust z and radius as stacks and slices are drawn. */
	double r;
	double x,y,z;
	/* Pre-computed circle */
	double *sint1,*cost1;
	double *sint2,*cost2;
	createCircleTable(&sint1,&cost1,-slices  );
	createCircleTable(&sint2,&cost2, stacks*2);

	/* Draw a line loop for each stack */
	for (i=1; i<stacks; i++)
	{
		z = cost2[i];
		r = sint2[i];
		glBegin(GL_LINE_LOOP);
		for(j=0; j<=slices; j++)
		{
			x = cost1[j];
			y = sint1[j];
			glNormal3d(x,y,z);
			glVertex3d(x*r*radiusX,y*r*radiusY,z*radiusZ);
		}
		glEnd();
	}

	/* Draw a line loop for each slice */
	for (i=0; i<slices; i++)
	{
		glBegin(GL_LINE_STRIP);
		for(j=0; j<=stacks; j++)
		{
			x = cost1[i]*sint2[j];
			y = sint1[i]*sint2[j];
			z = cost2[j];
			glNormal3d(x,y,z);
			glVertex3d(x*radiusX,y*radiusY,z*radiusZ);
		}
		glEnd();
	}

	/* Release sin and cos tables */
	free(sint1);
	free(cost1);
	free(sint2);
	free(cost2);
}

void drawSolidElipse(GLdouble radiusX, GLdouble radiusY, GLdouble radiusZ, GLint slices, GLint stacks)
{
	int i,j;
	glColor3f(1,0,0);
	/* Adjust z and radius as stacks are drawn. */
	double z0,z1;
	double r0,r1;

	/* Pre-computed circle */
	double *sint1,*cost1;
	double *sint2,*cost2;

	createCircleTable(&sint1,&cost1,-slices);
	createCircleTable(&sint2,&cost2,stacks*2);

	/* The top stack is covered with a triangle fan */
	z0 = 1.0;
	z1 = cost2[(stacks>0)?1:0];
	r0 = 0.0;
	r1 = sint2[(stacks>0)?1:0];


	glBegin(GL_TRIANGLE_FAN);

	glNormal3d(0,0,1);
	glVertex3d(0,0,radiusZ);

	for (j=slices; j>=0; j--)
	{
		glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
		glVertex3d(cost1[j]*r1*radiusX, sint1[j]*r1*radiusY, z1*radiusZ);
	}

	glEnd();

	/* Cover each stack with a quad strip, except the top and bottom stacks */
	for( i=1; i<stacks-1; i++ )
	{
		z0 = z1; z1 = cost2[i+1];
		r0 = r1; r1 = sint2[i+1];

		glBegin(GL_QUAD_STRIP);
		for(j=0; j<=slices; j++)
		{
			glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
			glVertex3d(cost1[j]*r1*radiusX, sint1[j]*r1*radiusY, z1*radiusZ);
			glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
			glVertex3d(cost1[j]*r0*radiusX, sint1[j]*r0*radiusY, z0*radiusZ);
		}
		glEnd();
	}

	/* The bottom stack is covered with a triangle fan */
	z0 = z1;
	r0 = r1;

	glBegin(GL_TRIANGLE_FAN);

	glNormal3d(0,0,-1);
	glVertex3d(0,0,-radiusZ);

	for (j=0; j<=slices; j++)
	{
		glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
		glVertex3d(cost1[j]*r0*radiusX, sint1[j]*r0*radiusY, z0*radiusZ);
	}

	glEnd();
	/* Release sin and cos tables */
	free(sint1);
	free(cost1);
	free(sint2);
	free(cost2);
}

// Hinh tru day la elipse
void drawSolidElipseCylinder(GLdouble radiusX, GLdouble radiusY, GLdouble height, GLint slices)
{
	glColor3f(1,1,0);
	int i,j;
	GLint stacks = 1;
	/* Step in z and radius as stacks are drawn. */

	double z0,z1;
	const double zStep = height / ( ( stacks > 0 ) ? stacks : 1 );

	/* Pre-computed circle */

	double *sint,*cost;

	createCircleTable(&sint,&cost,-slices);

	/* Cover the base and top */

	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0, 0.0, -1.0 );
	glVertex3d(0.0, 0.0,  0.0 );
	for (j=0; j<=slices; j++)
		glVertex3d(cost[j]*radiusX, sint[j]*radiusY, 0.0);
	glEnd();

	glBegin(GL_TRIANGLE_FAN);
	glNormal3d(0.0, 0.0, 1.0   );
	glVertex3d(0.0, 0.0, height);
	for (j=slices; j>=0; j--)
		glVertex3d(cost[j]*radiusX, sint[j]*radiusY, height);
	glEnd();

	/* Do the stacks */

	z0 = 0.0;
	z1 = zStep;

	for (i=1; i<=stacks; i++)
	{
		if (i==stacks)
			z1 = height;

		glBegin(GL_QUAD_STRIP);
		for (j=0; j<=slices; j++ )
		{
			glNormal3d(cost[j],				sint[j],        0.0 );
			glVertex3d(cost[j]*radiusX,		sint[j]*radiusY, z0  );
			glVertex3d(cost[j]*radiusX, 		sint[j]*radiusY, z1  );
		}
		glEnd();

		z0 = z1; z1 += zStep;
	}

	/* Release sin and cos tables */

	free(sint);
	free(cost);
}

void drawSolidElipseCylinderRotated(GLdouble radiusX, GLdouble radiusY, GLdouble height, GLint slices)
{
	glPushMatrix();
	glRotatef(-90,1,0,0);
	drawSolidElipseCylinder(radiusX, radiusY, height, slices);
	glPopMatrix();
}

#pragma endregion simpledrawing

#pragma region cylinderdrawing
void renderCylinder(float x1, float y1, float z1, float x2,float y2, float z2, float radius,int subdivisions,GLUquadricObj *quadric)
{
	float vx = x2-x1;
	float vy = y2-y1;
	float vz = z2-z1;
	float v = sqrt( vx*vx + vy*vy + vz*vz );
	double ax;

	if (fabs(vz) < 1.0e-3) {
		ax = 57.2957795*acos( vx/v ); // rotation angle in x-y plane
		if ( vy <= 0.0 )
			ax = -ax;
	}
	else {
		ax = 57.2957795*acos( vz/v ); // rotation angle
		if ( vz <= 0.0 )
			ax = -ax;
	}

	float rx = -vy*vz;
	float ry = vx*vz;

	glPushMatrix();
	//draw the cylinder body
	glTranslatef( x1,y1,z1 );
	if (fabs(vz) < 1.0e-3) {
		glRotated(90.0, 0, 1, 0.0); // Rotate & align with x axis
		glRotated(ax, -1.0, 0.0, 0.0); // Rotate to point 2 in x-y plane
	}
	else {
		glRotated(ax, rx, ry, 0.0); // Rotate about rotation vector
	}
	gluQuadricOrientation(quadric,GLU_OUTSIDE);
	gluCylinder(quadric, radius, radius, v, subdivisions, 1);

	//draw the first cap
	gluQuadricOrientation(quadric,GLU_INSIDE);
	gluDisk( quadric, 0.0, radius, subdivisions, 1);
	glTranslatef( 0,0,v );

	//draw the second cap
	gluQuadricOrientation(quadric,GLU_OUTSIDE);
	gluDisk( quadric, 0.0, radius, subdivisions, 1);
	glPopMatrix();
}

void renderCylinder_convenient(float x1, float y1, float z1, float x2,float y2, float z2, float radius,int subdivisions)
{
	//the same quadric can be re-used for drawing many cylinders
	GLUquadricObj *quadric=gluNewQuadric();
	gluQuadricNormals(quadric, GLU_SMOOTH);
	renderCylinder(x1,y1,z1,x2,y2,z2,radius,subdivisions,quadric);
	gluDeleteQuadric(quadric);
}

#pragma endregion cylinderdrawing

#pragma region testBug

void drawFrustum(float width,float height,float frush_Near,float frush_Far)
{
	frush_Near*=(-1);
	frush_Far*=(-1);
	glColor3f(1,0,0);
	glBegin(GL_LINE_LOOP);
	glVertex3f(-width/2,-height/2,frush_Far);
	glVertex3f(-width/2,height/2,frush_Far);
	glVertex3f(width/2,height/2,frush_Far);
	glVertex3f(width/2,-height/2,frush_Far);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3f(-width/2,-height/2,frush_Near);
	glVertex3f(-width/2,height/2,frush_Near);
	glVertex3f(width/2,height/2,frush_Near);
	glVertex3f(width/2,-height/2,frush_Near);
	glEnd();

	glBegin(GL_LINES);
	glVertex3f(-width/2,-height/2,frush_Far);
	glVertex3f(-width/2,-height/2,frush_Near);

	glVertex3f(-width/2,height/2,frush_Far);
	glVertex3f(-width/2,height/2,frush_Near);

	glVertex3f(width/2,height/2,frush_Far);
	glVertex3f(width/2,height/2,frush_Near);

	glVertex3f(width/2,-height/2,frush_Far);
	glVertex3f(width/2,-height/2,frush_Near);

	glEnd();
}

#pragma endregion testBug